/*
 * Copyright 2007 Jaisy Sprl.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package be.jaisy.ui.gwt.widget.client.viewer.table.internal;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import be.jaisy.ui.gwt.widget.client.viewer.ContentViewer;
import be.jaisy.ui.gwt.widget.client.viewer.table.ITableLabelProvider;

import com.google.gwt.user.client.ui.ChangeListener;
import com.google.gwt.user.client.ui.ClickListener;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.KeyboardListenerAdapter;
import com.google.gwt.user.client.ui.MultiWordSuggestOracle;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.SuggestOracle;
import com.google.gwt.user.client.ui.Widget;

/**
 * Helper class for filtering
 * 
 * @author Stany Blanvalet <stany.blanvalet@jaisy.be>
 */
public class FilterTableViewer{

	private static final int FILTER_ROW_INDEX = 1;
	private static final String FILTER_ROW_STYLE = "-filter";
	
	/**
	 * A map from the viewer's filter elements to SuggestBox. 
	 * key type: columnIndex <code>Integer</code>.
	 * value type: <code>SuggestBox</code>. 
	 */
	private Map/* <Integer, SuggestBox> */filterMap;

	protected ContentViewer contentViewer;

	public FilterTableViewer(ContentViewer contentViewer){
		super();
		this.contentViewer = contentViewer;
	}
	
	/**
	 * This method is reponsible for the filter row creation
	 * @param tableCellProvider provide method impl to adapt real table.
	 */
	public void provideFilters(ITableCellProvider tableCellProvider){
		if (hasFilters()) {
			for (Iterator iter = filterMap.entrySet().iterator(); iter.hasNext();) {
				Entry entry = (Entry) iter.next();
				Integer key = (Integer) entry.getKey();
				tableCellProvider.provideData(FILTER_ROW_INDEX, key.intValue(), entry.getValue());
				tableCellProvider.provideStyle(FILTER_ROW_INDEX, FILTER_ROW_STYLE);				
			}
		}
	}

	/**
	 * Adds the given filter to this viewer on this column.
	 * 
	 * @param filter
	 *           a viewer filter
	 */
	public void addFilter(int columnIndex){
		if (filterMap == null){
			filterMap = new HashMap/* <Integer, SuggestBox> */();
		}

		final SuggestBox suggestBox = new SuggestBox(new MultiWordSuggestOracle());
		suggestBox.setWidth("100%");
		suggestBox.addKeyboardListener(new KeyboardListenerAdapter(){

			public void onKeyDown(Widget sender, char keyCode, int modifiers){
				if (keyCode == KeyboardListener.KEY_ENTER){
					contentViewer.internalRefresh();		
				}
			}
		});

		filterMap.put(new Integer(columnIndex), suggestBox);
	}

	/**
	 * Add proposal to the specific filter box
	 * 
	 * @param columnIndex
	 * @param suggestion, String
	 */
	public void addSuggestion(int columnIndex, String suggestion){
		SuggestBox suggestBox = getSuggestBox(columnIndex);
		if (suggestBox != null){
			SuggestOracle suggestOracle = suggestBox.getSuggestOracle();
			if (suggestOracle != null && (suggestOracle instanceof MultiWordSuggestOracle))
				((MultiWordSuggestOracle)suggestOracle).add(suggestion);
		}
	}

	/**
	 * clear all proposals from the specific filter box
	 * 
	 * @param columnIndex
	 */
	public void clearSuggestion(int columnIndex){
		filterMap.put(new Integer(columnIndex), new SuggestBox(new MultiWordSuggestOracle()));
	}

	/**
	 * Returns whether this viewer has any filters.
	 * 
	 * @return boolean
	 */
	public boolean hasFilters(){
		return filterMap != null && filterMap.size() > 0;
	}

	/**
	 * Returns whether this specific column has any filters.
	 * 
	 * @return boolean
	 */
	public boolean hasFilters(int col){
		return hasFilters() && filterMap.get(new Integer(col)) != null;
	}
	
	/**
	 * Filter all elements
	 * 
	 * @param elements
	 * @return filtered elements
	 */
	public Object[] filter(Object[] elements){
		if (!hasFilters())
			return elements;
		Object[] filteredElement = elements;
		for (Iterator iter = filterMap.keySet().iterator(); iter.hasNext();){
			filteredElement = filter(((Integer)iter.next()).intValue(), filteredElement);
		}
		return filteredElement;
	}

	private SuggestBox getSuggestBox(int columnIndex){
		if (filterMap == null)
			return null;
		return (SuggestBox)filterMap.get(new Integer(columnIndex));
	}

	/**
	 * Returns the result of running the given elements through the filters.
	 * 
	 * @param elements
	 *           the elements to filter
	 * @return only the elements which all filters accept
	 */
	private Object[] filter(int columnIndex, Object[] elements){
		if (filterMap == null)
			return elements;
		SuggestBox suggestBox = (SuggestBox)filterMap.get(new Integer(columnIndex));
		if (suggestBox == null || suggestBox.getText() == null)
			return elements;
		ArrayList filtered = new ArrayList();
		for (int i = 0; i < elements.length; i++){
			Object cell = ((ITableLabelProvider)contentViewer.getLabelProvider()).getRow(elements[i])[columnIndex];
			if (cell != null && cell instanceof String){ 
				if(((String)cell).indexOf(suggestBox.getText()) >= 0)
					filtered.add(elements[i]);
			}else 
				filtered.add(elements[i]);
				
		}
		return filtered.toArray();
	}
}